# Live-only QML units

Units/files that cannot be covered headlessly (and why). These are out of scope for the
headless QML gate and are the target of the live-dock capture (P3). Add entries during
curation/expansion instead of gaming a headless test.

- `plasmoid/package/contents/ui/ContextMenu.qml` — SIGSEGVs in C++ during QML finalize on construct.
- `shell/package/contents/views/WidgetExplorer.qml` — SIGSEGVs in C++ during construction.
- `plasmoid/package/contents/ui/main.qml` — `Plasmoid` attached object (PlasmoidAttached)
  can't be created without a live Applet/Containment; object can't be retained.
- `plasmoid/package/contents/ui/task/TaskItem.qml` (partial) — handlers reading `root`,
  `tasksModel`, `scrollableList`, and the `ListView` delegate `index` need a live
  containment/ListView; the pure helpers are headless-coverable.

## Added during draft curation (2026-06-09)

- shell/package/contents/configuration/pages/AppearanceConfig.qml :: the three length-row ScrollArea onClicked handlers (Math.round of maxLength@351, minLength@434, offset@566) — MouseArea.clicked takes a QQuickMouseEvent; emitting it no-arg or with a QtObject mock throws in Qt6 ("Could not convert argument 0 to QQuickMouseEvent" / "Passing incompatible arguments to signals is not supported"), so they need a real pointer event from a live config view.
- containment/package/contents/ui/applet/AppletItem.qml::activateAppletForNeutralAreas — its only side-effecting branch calls latteView.extendedInterface.toggleAppletExpanded(applet.plasmoid.id); needs a live latteView + real applet to assert anything beyond the no-op reject branch.
- containment/package/contents/ui/applet/AppletItem.qml::onXChanged / onYChanged — the translAnim path requires root.dragOverlay.currentApplet and root.dragInfo drag state from a live containment in configure-applets drag mode; headless they take the early-return with no observable effect.
- containment/package/contents/ui/applet/AppletItem.qml::viewSignalsConnector onMousePressed/onMouseReleased/onWheelScrolled — Connections target root.latteView and emit the dock's mouse/wheel signals; need a live LatteView.
- containment/package/contents/ui/applet/AppletItem.qml::onExpandedAppletStateChanged — Connections target root.latteView.extendedInterface; needs a live dock with hasExpandedApplet state.
- containment/package/contents/ui/applet/AppletItem.qml::Component.onDestruction@606 — ticks at teardown but its reads (animations.needBothAxis.removeEvent + root signal disconnects) only run during incubation cleanup; not cleanly assertable as an observable effect headless.
- containment/package/contents/ui/applet/AppletItem.qml::scrollDelayer onTriggered@995 — single-line expression handler, NOT instrumented by the coverage tool (no Cov.tick injected), so it earns no unit; its behavior (blockWheel clears) is still asserted by test_scrollDelayTimer_clearsBlockWheel but cannot be a counted instrumented unit.
- containment/package/contents/ui/abilities/AutoSize.qml::onIsActiveChanged@56 — fires only when isActive changes value; isActive is permanently false headlessly because it short-circuits on root.behaveAsDockWithMask=false. Forcing it truthy requires dereferencing Plasmoid.configuration.autoSizeEnabled, which throws without a live containment, so the transition can't be driven headlessly.
- containment/package/contents/ui/abilities/AutoSize.qml::updateIconSize@152 grow/shrink math (the main if-block, lines ~158-242, incl. the in-body addPrediction/producesEndlessLoop calls) — gated on sizer.isActive being truthy, which needs Plasmoid.configuration; only the function entry + the !isActive restore branch are reachable headlessly (and are covered).
- shell/package/contents/configuration/pages/BehaviorConfig.qml: alignment onPressedChanged@288/@304/@320/@337 and visibility onPressedChanged@378/@393/@408 — the handler effect (setNextLocation for alignment, latteView.visibility.mode= for visibility) runs only inside `if (pressed)`, and the button's `pressed` is a read-only derived property. The offscreen synthetic-mouse pipeline never delivers a real press (verified: mouseClick on the page's buttons records 0 setNextLocation calls), so emitting pressedChanged() leaves pressed=false and produces no observable effect. Need a live config dialog with a real input/window to drive pressed=true.
- shell/package/contents/controls/CustomIndicatorButton.qml :: onVisibleChanged@99 (combobox popup's visible handler) — needs a live popup that stays open. Headless the T.Popup self-closes, so inside the handler `visible` reads false and the inner selectChosenType() never runs; only the entry tick fires (banking it is banned). Its meaningful work (selectChosenType, which IS independently covered+asserted) requires a live dock/popup.
- shell/package/contents/configuration/pages/EffectsConfig.qml: the shadow-color buttons' onPressedChanged (@187 DefaultColorShadow / @206 ThemeColorShadow / @228 UserColorShadow) and the animation-duration buttons' onPressedChanged (@338 x1 / @352 x2 / @366 x3) only write plasmoid.configuration on the press half of a real pointer cycle (if(pressed)). PlasmaComponents.Button.pressed is read-only and not settable headlessly; firing it needs a laid-out, geometry-bearing button in a live config dialog, so these write branches are live-only.
- shell/package/contents/configuration/pages/EffectsConfig.qml: the sliders' onPressedChanged@91/@138 only delegate to updateShadowSize/updateShadowOpacity on a real slider press; the functions themselves are covered directly, but the press-driven handler entry needs a live pointer cycle.
- containment/package/contents/ui/applet/ItemWrapper.qml: the length/thickness computed-property branch bodies (autofill-dragging via root.dragOverlay.currentApplet, internalSplitterComputedLength using appletItem.layouter.start/endLayout.lengthWithoutSplitters, layouter.maxMetricsInHigherPriority) need a live containment with a real drag overlay and layouter to drive each branch
- containment/package/contents/ui/applet/ItemWrapper.qml: the Connections handler onAppletRequestedVisualIndicator (target root.latteView.extendedInterface) needs a live latteView whose extendedInterface emits appletRequestedVisualIndicator; the Connections target is null headlessly so it never connects
- containment/package/contents/ui/applet/ItemWrapper.qml: onIsRoundToIconSizeEnabledChanged body (sets communicator.appletIconItem.roundToIconSize) needs a live communicator with a discovered applet IconItem and the _overlayIconLoader active (communicator.appletMainIconIsFound && indicators.info.needsIconColors)
- shell/package/contents/configuration/LatteDockConfiguration.qml::onActivated@529 — the actions-combo dispatch (add: -> latteView.newView, duplicate: -> latteView.duplicateView). Emitting activated(index) manually on the LatteComponents.ComboBox (a QtQuick.Templates.ComboBox) does not reach the Connections{ function onActivated } handler headlessly (no Cov.tick fires, no mock side-effect); needs a real popup row selection.
- shell/package/contents/configuration/LatteDockConfiguration.qml::onCheckedChanged@307/@327/@339 page swaps via QQC2.StackView — the tab buttons' replace(page) handlers. The StackView never settles offscreen (depth stays 0, currentItem null even after 600ms) because the child config pages (BehaviorConfig/AppearanceConfig/EffectsConfig) throw on their own live-only ambient reads during the push transition, so no page is ever current to assert a swap against. Needs a live dialog with real pages.
- shell/package/contents/configuration/LatteDockConfiguration.qml::onClicked@277 advanced-label MouseArea (toggles advancedSwitch.checked) — not uniquely identifiable among ~40 sibling MouseAreas in the subtree, and its only effect (flipping the advanced switch) is already covered directly via the switch in test_advancedSwitchWritesSetting. Needs a real hover/click on the label.
- plasmoid/package/contents/ui/abilities/Launchers.qml::currentStoredLauncherList — dereferences the attached Plasmoid.configuration.launchers59 singleton (and activityInfo for the activity-scoped records); the Plasmoid attached object only exists inside a real applet and can't be shadowed by a lowercase id/property, so the config-fallback branch can't run headlessly
- plasmoid/package/contents/ui/abilities/Launchers.qml::importLauncherListInModel — same: assigns tasksModel.launcherList = Plasmoid.configuration.launchers59, throws on the attached-object dereference without a live applet
- containment/package/contents/ui/abilities/privates/LayouterPrivate.qml::initializationPhase — reads unqualified `root.myView.alignment` + `LatteCore.Types.Justify` and drives `startLayout.grid`/`mainLayout.grid`/`endLayout.grid`, which only populate when the live containment injects `root` and a non-null `layouts` model; no QML-only mock supplies these headlessly.
- containment/package/contents/ui/abilities/privates/LayouterPrivate.qml::updateFillAppletsWithOneStep — reads `contentsMaxLength` (→ `root.maxLength` + `background.totals.paddingsLength`) and `root.minLength`, plus the live layout sub-objects; needs a live dock/containment context.
- containment/package/contents/ui/abilities/privates/LayouterPrivate.qml::updateFillAppletsWithTwoSteps — same `root`/`background` context dependence plus per-layout `fillApplets`/`sizeWithNoFillApplets`/`grid.length` from a populated layouts model; live-only.
- containment/package/contents/ui/abilities/privates/LayouterPrivate.qml::_updateSizeForAppletsInFill — gated on unqualified `inNormalFillCalculationsState` (defined on the live Layouter ability) and `root.myView.alignment`; can't be exercised headlessly.
- plasmoid/package/contents/ui/main.qml — PlasmoidItem root; its root bindings (Plasmoid.backgroundHints + the whole Plasmoid.configuration.*/formFactor/location cascade) need the C++ PlasmaQuick::PlasmoidAttached object, which Qt only creates when the item is the live root of an applet/containment. Headlessly the runtime emits 'Detected a PlasmoidItem which is not the root QML item: this is not supported', Plasmoid resolves to undefined, and createObject() returns null. No retained instance means none of its functions (getBadger, updateBadge, containsMouse, launcherExists, taskExists, previewContainsMouse, createContextMenu, mpris2 startOperation/goNext/play/pause/stop/raise/quit, sourceNameForLauncherUrl) or handlers can be called or asserted. Plasmoid is a qualified attached property (import org.kde.plasma.plasmoid), not an unqualified creation-context name, so the id:root mock pattern cannot supply it. Needs a live dock/applet (latte containment with a real Plasmoid applet backing).
- declarativeimports/abilities/client/ParabolicEffect.qml::Component.onDestruction@64 — the destruction handler's only effect (clear bridge.parabolic.client, disconnect signal handlers) fires during temp-object teardown where no test assertion can observe it headlessly; its observable effect (host client unset) needs a live retained host with an observed teardown.
- plasmoid/package/contents/ui/taskslayout/ScrollableList.qml :: onContentsExtraSpaceChanged clamp side-effect — the handler runs once at construction (asserted via test_01's contentsExtraSpace/scrollLastPos inputs), but its actual clamp (re-firing on a later span change and pulling contentX/contentY back inside bounds) does not reproduce headlessly. Instrumented and confirmed: with the QtObject-wrapped context the readonly contentsExtraSpace re-evaluates (600->100->0) but its change signal never re-invokes the handler, so contentX stays at the out-of-bounds value (590) and no second tick fires. Asserting the clamp needs the real Binding-driven length/tasksLength of a live dock.
- plasmoid/package/contents/ui/task/TaskItem.qml::generateSubText@547 - first statement dereferences Plasmoid.configuration.showOnlyCurrentDesktop; the attached Plasmoid object has no configuration without a real applet, so the body throws before any assertable work.
- plasmoid/package/contents/ui/task/TaskItem.qml::slotPublishGeometries@637 - needs real backend.globalRect geometry + a live scrollableList/myView and canPublishGeometries true (depends on real visible/zoom/size); only assertable against a live dock.
- plasmoid/package/contents/ui/task/TaskItem.qml::onIsDraggedChanged@326 - drag start path calls contentItem.monochromizedItem.grabToImage and dragHelper.Drag.* attached-property idiom; requires a live render/Drag context.
- plasmoid/package/contents/ui/task/TaskItem.qml::showPreviewWindow@472 and activateLauncher@415/activateTask@429 group-parent branches - exercise windowsPreviewDlg.show, LatteCore.WindowSystem.compositingActive and backend.windowViewAvailable/canPresentWindows window-view paths that need a live compositor/backend (the non-group activate branch IS covered headlessly).
- shell/package/contents/configuration/pages/TasksConfig.qml — launcher-group button onPressedChanged handlers (lines 380/401/419): their only observable effect is writing launchersGroup inside `if (pressed)`, and a QQC2 Button's `pressed` cannot be forced true offscreen (no real pointer grab; mousePress fails 'window not shown' for a Loader/createTemporaryObject page, and setting down=true leaves pressed=false). Needs a live dock with a real pointer.
- shell/package/contents/configuration/pages/TasksConfig.qml — combo onCurrentIndexChanged handlers (lines 498/559/624): config write-back only fires through real popup interaction. Imperatively assigning currentIndex neither re-runs the handler (tick count stays at the single construction tick) nor writes back to tasks.configuration, and flipping the bound config value doesn't move currentIndex headlessly. Needs a live ComboBox popup.
- containment/package/contents/ui/VisibilityManager.qml: the per-edge geometry branches of updateMaskArea@250 and updateInputGeometry@324 (Top/Bottom/Left/Right) need a live Plasmoid.location; offscreen it is undefined so the if/else chains fall through and the edge-specific localGeometry/inputMask values are not observable. The function bodies still tick (no-edge path is asserted), but edge geometry is live-only.
- containment/package/contents/ui/VisibilityManager.qml: slidingOutToPos@45 panel branch and slidingOutRealFloating's PropertyAnimation `to:`@608 read Plasmoid.configuration.screenEdgeMargin, which is undefined without a live containment (caught binding TypeError, value keeps the non-panel default), so the panel screen-edge-margin computation is live-only.

## Added during expansion batch 1 (2026-06-09)

- plasmoid/package/contents/ui/PulseAudio.qml::mute@98 / unmute@101 / increaseVolume@105 / decreaseVolume@110 — these live in the Instantiator delegate (the per-stream QtObject). Headlessly the delegate's PulseObjectFilterModel/SinkInputModel has no active sink-inputs (instantiator.count==0), so no delegate object materialises and the functions can't be called; they need a live PulseAudio session with playing streams.
- plasmoid/package/contents/ui/PulseAudio.qml::findStreams@44 inner match branch (stream[key]===value / appName push) and streamsForPid@59 fallback loop (backend.parentPid match) — the loop bodies only run when instantiator.count>0; with no live stream the loops never iterate (function entries + empty returns ARE covered), so the per-stream branches are live-only.
- plasmoid/package/contents/ui/PulseAudio.qml::Component.onCompleted@120 — fires at construction (tick confirmed) but its only effect is a console.log with no observable/assertable side-effect, so it is not claimed per the real-assertion rule.
- declarativeimports/abilities/client/indicators/LatteIndicator.qml::onStateHeightChanged@171 — its body (height = stateHeight) runs only when firstPoint.vertical is true, and vertical is Plasmoid.formFactor===Vertical, undefined without a live containment so permanently false headlessly. Needs a live dock with a vertical formFactor.
- declarativeimports/abilities/client/indicators/LatteIndicator.qml::Component.onDestruction@186 — fires only at temp-object teardown; its sole effect (disconnect indicator.currentIconSizeChanged) is unobservable during incubation cleanup. Needs a live retained host with an observed teardown.
- containment/package/contents/ui/layouts/ParabolicEdgeSpacer.qml::Component.onDestruction@106 — ticks at teardown; its only effects (parabolic.sglClearZoom/sglUpdateLowerItemScale/sglUpdateHigherItemScale disconnect) fire during incubation cleanup where no assertion can observe them. Mirrors ParabolicEffect.qml::Component.onDestruction@64.
- declarativeimports/abilities/definition/ParabolicEffect.qml::applyParabolicEffect@37 reversed/RightToLeft swap branch (lines 56-60) — gated on `Qt.application.layoutDirection === Qt.RightToLeft && Plasmoid.formFactor === Horizontal`. Headlessly layoutDirection is LeftToRight so the && short-circuits, and forcing it needs a RightToLeft live view plus the Plasmoid attached formFactor; the left/right scale swap can't be driven offscreen.
- shell/package/contents/views/Panel.qml::Component.onDestruction@69 — its only body is a console.log; the handler ticks at teardown but produces no observable, assertable effect, so claiming it would only bank a teardown entry tick.
- declarativeimports/components/ComboBox.qml::onGenericPressed@153 / onGenericReleased@161 / onCanceled@134 / onClosed@147 / onPressedChanged@80 / onPressedChanged@190 — every handler writes control.pressed, which is read-only on QtQuick.Templates.ComboBox in Qt 6.11 (TypeError "Cannot assign to read-only property pressed"), so the body throws the moment the handler is invoked and an entry tick would bank while it fails (banned). The framework only drives `pressed` on a real pointer press over a live popup, so the whole file is live-only (no test written).
- declarativeimports/abilities/client/ThinTooltip.qml::Component.onDestruction@55 — clears bridge.thinTooltip.client during temp-object teardown, unobservable headlessly (fires after the test body returns); same pattern as ParabolicEffect.qml::Component.onDestruction@64. Needs a live retained host with an observed teardown.

## Added during expansion batch 2 (2026-06-09)

- containment/package/contents/ui/background/MultiLayered.qml::Component.onDestruction@427 (solidBackground) — its only body is root.updateEffectsArea.disconnect(updateEffectsArea), which fires during temp-object teardown/incubation cleanup; the disconnect produces no observable, assertable effect headlessly (mirrors the established Component.onDestruction live-only pattern).
- containment/package/contents/ui/background/MultiLayered.qml::onThemeChanged@436 — lives in a Connections whose target is the unqualified `themeExtended` (null headlessly, so it never connects/fires), and its body writes Plasmoid.configuration.panelShadows. Plasmoid is a qualified attached property (import org.kde.plasma.plasmoid), not a creation-context name, so it can't be shadowed by a mock and the write throws without a live applet/containment.
- containment/package/contents/ui/Upgrader.qml::Component.onCompleted@14 — fires on construction and calls v010_upgrade(); honest only as far as the entry tick. v010_upgrade then reads Plasmoid.configuration.shadowsUpgraded@22 which throws (TypeError: Cannot read property 'shadowsUpgraded' of undefined) because Plasmoid is a qualified attached property (import org.kde.plasma.plasmoid) that cannot be shadowed by a creation-context mock, so the body fails immediately after the entry tick (banking it is banned). Needs a live containment with a real Plasmoid applet.
- containment/package/contents/ui/Upgrader.qml::v010_upgrade@18 — entry tick fires (root.upgrader_v010_alignment() is mockable), but the very next statement dereferences Plasmoid.configuration.shadowsUpgraded@22 and throws TypeError on the undefined Plasmoid attached object; the shadow/tasks-migration body and the v010_tasksMigrateTimer.start() arming are all unreachable headlessly. Needs a live containment with a real Plasmoid applet.
- containment/package/contents/ui/Upgrader.qml::Component.onCompleted@44 (v010_tasksRepeater migration delegate) — never ticks: the Repeater model `latteView && !Plasmoid.configuration.tasksUpgraded ? latteView.extendedInterface.latteTasksModel : null` throws on Plasmoid.configuration.tasksUpgraded@42 and resolves to null, so no delegate instantiates; the body would also copy every Plasmoid.configuration.* key into tasks.configuration.*. Needs a live latteView with a populated latteTasksModel plus live Plasmoid and tasks applets.
- containment/package/contents/ui/Upgrader.qml::onTriggered@95 (v010_tasksMigrateTimer) — never ticks: the timer is only armed by v010_tasksMigrateTimer.start() inside v010_upgrade, which throws on Plasmoid.configuration before reaching it; the handler itself writes Plasmoid.configuration.tasksUpgraded. Needs a live containment with a real Plasmoid applet.
- plasmoid/package/contents/ui/abilities/launchers/Syncer.qml::Component.onDestruction@28 — ticks at temp-object teardown; its only effect (bridge.launchers.host.removeAbilityClient(_syncer)) fires during incubation cleanup after the test body returns, so no assertion can observe it headlessly. Needs a live retained host with an observed teardown.
- plasmoid/package/contents/ui/abilities/launchers/Syncer.qml::onIsActiveChanged@34 (else-if removeAbilityClient branch only) — the add branch IS covered, but the else-if remove path is unreachable headlessly without an NPE: isActive only falls to false when bridge or bridge.launchers.host is null, yet the else-if then calls bridge.launchers.host.removeAbilityClient on a null host (TypeError). Driving the remove branch needs a live bridge whose host detaches while staying callable.
- plasmoid/package/contents/ui/abilities/launchers/Syncer.qml::clientId binding@17 — readonly property int clientId: Plasmoid.id reads a qualified attached property (import org.kde.plasma.plasmoid), which cannot be shadowed by a creation-context mock and resolves to undefined headlessly (binding warning). Not a Cov-instrumented unit; live-only, needs a real Plasmoid applet.
- shell/package/contents/controls/IndicatorConfigUiManager.qml::showNextIndicator@27 — a method on the internal hiddenIndicatorPage Item, not exposed on the uiManager root; C++ (IndicatorUiManager::showNextIndicator) stores the page via setParentItem and invokes it by QMetaObject::invokeMethod. Headless the test holds only the root, the page has no parented indicator children (children.length is 0) and there is no live stackView with replace()/forwardSliding/currentItem/Layout, so both branches no-op and nothing is observable. Needs a live config dialog with real indicator config UIs.
- declarativeimports/abilities/bridge/PositionShortcuts.qml::Component.onDestruction@47 — fires only at temp-object teardown; its sole effect (disconnect host.sglActivateEntryAtIndex/sglNewInstanceForEntryAtIndex from the client) runs during incubation cleanup where no test assertion can observe it. Needs a live retained host/client with an observed teardown.
- declarativeimports/abilities/bridge/PositionShortcuts.qml::onIsConnectedChanged@12 disconnect (else) branch — isConnected (host && client) can only go true->false when host or client just became null, but the else body then dereferences that null operand (host.sglActivateEntryAtIndex.disconnect(client.sglActivateEntryAtIndex)) and throws; the branch is structurally undrivable headlessly without swallowing the throw (banned). The connect (true) branch and the entry tick ARE covered+asserted; only the disconnect branch needs a live host/client lifecycle.
- declarativeimports/abilities/client/PositionShortcuts.qml::Component.onDestruction@52 — teardown-only handler; its sole effect (bridge.shortcuts.client = null) fires during temp-object incubation cleanup after the test body has returned, so no assertion can observe it headlessly. Mirrors ParabolicEffect.qml::Component.onDestruction@64 and ThinTooltip.qml::Component.onDestruction@55. Needs a live retained host with an observed teardown.
- containment/package/contents/ui/applet/communicator/Engine.qml::onAppletChanged@68 latteBridge-discovery branch — when the applet carries a latteBridge property, checkAndUpdateAppletRootItem sets appletDiscoveredRootItem=applet, which makes appletContainsLatteBridge true and activates the bridgeLoader; LatteBridge.qml then throws on live-only containment bindings (publicApi/factor/margin/iconSize of undefined). Asserting the discovered-root-item write needs a live containment/applet backing the bridge. The handler's headless branch (applet present, no bridge -> arms overlayInitTimer) IS covered.
- containment/package/contents/ui/applet/communicator/Engine.qml::reconsiderAppletIconItem@59 — the function ticks and runs headlessly, but its only observable effect (setting communicator.appletIconItem to a discovered IconItem via AppletIdentifier.identifyGeneric/identifyKicker/etc.) needs a mock applet child tree whose toString() matches 'CompactRepresentation('/'IconItem('. With the empty-children generic mock the identify walk finds nothing and appletIconItem stays at its initial null, so no positive effect is assertable; the null->null compare passes even with the call removed. The discovery branch (appletDiscoveredRootItem via a real latteBridge) additionally activates bridgeLoader/LatteBridge, which reads a live containment and is already live-only.
- plasmoid/package/contents/ui/previews/ToolTipDelegate2.qml::instanceAtPos@135 loop body (matching a real preview instance) — the per-instance mapFromItem/bounding-box hit branch only runs when contentItem holds materialised ToolTipInstance delegates; headlessly the preview Loader is kept inactive (isLauncher=true) because ToolTipInstance dereferences mpris2Source/appletAbilities.myView/toolTipDelegate from a live dock, so contentItem.children is empty and the loop never iterates. The function entry + empty-loop null return ARE covered+asserted; the instance-match branch needs a live group ListView with real preview delegates.
- plasmoid/package/contents/ui/previews/ToolTipDelegate2.qml::onDragMove (DropArea, ~line 90) — arrow-function handler, NOT instrumented by the coverage tool (no Cov.tick injected), so it earns no counted unit; its body also calls mainToolTip.instanceAtPos + tasksModel.requestActivate(current.submodelIndex), which need a real DeclarativeDragDropEvent with valid x/y over a materialised preview instance from a live drag session.

## Added 2026-06-25

- declarativeimports/components/SpriteRectangle.qml — the file's only instrumented unit is onPaint@56, a Canvas paint handler that never fires offscreen (no requestPaint/grabToImage forces it). Everything else is readonly property bindings, which the instrumenter does not tick. A headless test can assert the binding math but earns zero counted coverage, so this file is live-only until the sprite is rendered in a live view. (An exact-value binding test was written and rejected for claiming units it cannot earn.)

## Added 2026-07-11

- `Storage::modelFromLive` and the active branches of the four validation methods
  (`hasDifferentAppletsWithSameId`, `hasAppletsAndContainmentsWithSameId`,
  `hasOrphanedParentAppletOfSubContainment`, `hasOrphanedSubContainments`) — the active
  path walks live `Plasma::Containment`/`Plasma::Applet` objects from a running Corona.
  No headless test can construct these; exercised only under live capture.
